package com.itdct.onflow.generator.template

import com.itdct.onflow.core.entity.BaseUserEntity
import com.itdct.onflow.generator.base.BaseTemplate
import com.itdct.onflow.generator.config.GeneratorConfig
import com.itdct.onflow.generator.constant.GroupEnum
import com.itdct.onflow.generator.model.ClassModel
import com.itdct.onflow.generator.model.CompareResultModel
import com.itdct.onflow.generator.model.FieldModel

/**
 * @author Zhouwx
 * @date 2025/8/12 15:50:41
 * @version 1.0
 * @description
 */
class MapperXmlTemplate(classModel: ClassModel, generatorConfig: GeneratorConfig) : BaseTemplate(classModel, generatorConfig) {
    private val returnValueFilter = ArrayList<String>()

    init {
        returnValueFilter.add("del")
        returnValueFilter.add("version")
        returnValueFilter.add("deleteUser")
        returnValueFilter.add("deleteTime")

        filterFieldList.add("del")
        filterFieldList.add("version")
        filterFieldList.add("deleteUser")
        filterFieldList.add("deleteTime")
        filterFieldList.add("createTime")
        filterFieldList.add("updateTime")
    }

    override fun render(): String {
        return """
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
${uuidPrint()}
<mapper namespace="${classModel.packageName}.mapper.${classModel.className}Mapper">
    <select id="findById" resultType="${classModel.packageName}.vo.${classModel.className}DetailVo">
        select
        ${rangeStart("idFields")}${selectFields()}${innerJoinFields()}${rangeEnd("idFields")}${createUpdateAccountName()}
        from ${classModel.tableName} as ${tableNameAbbr(classModel)}
        ${rangeStart("idJoins")}${innerJoinTables()}${rangeEnd("idJoins")}${createUpdateAccountJoin()}
        <where>
            ${tableNameAbbr(classModel)}.id = #{id}
        </where>
    </select>

    <select id="findByList" resultType="${classModel.packageName}.vo.${classModel.className}ListVo">
        select
        ${rangeStart("listFields")}${selectFields()}${innerJoinFields()}${rangeEnd("listFields")}${createUpdateAccountName()}
        from ${classModel.tableName} as ${tableNameAbbr(classModel)}
        ${rangeStart("listJoins")}${innerJoinTables()}${rangeEnd("listJoins")}${createUpdateAccountJoin()}
        <where>
            ${rangeStart("ifTest")}${ifTests()}${rangeEnd("ifTest")}
            <if test="createTime != null and createTime.size == 2">
                and ${tableNameAbbr(classModel)}.create_time between #{createTime[0]} and #{createTime[1]}
            </if>
            <if test="updateTime != null and updateTime.size == 2">
                and ${tableNameAbbr(classModel)}.update_time between #{updateTime[0]} and #{updateTime[1]}
            </if>
        </where>
        <if test="orderBy != null">order by ${"$"}{orderBy}</if>
        <if test="orderBy == null">order by ${tableNameAbbr(classModel)}.${orderByField()} desc</if>
    </select>
    ${rangeStart("selectFunctions")}
${selectFunctions()}
    ${rangeEnd("selectFunctions")}
</mapper>
        """.trimIndent()
    }

    private fun createUpdateAccountName(): String {
        val clazz = classModel.clazz ?: return ""
        if (BaseUserEntity::class.java.isAssignableFrom(clazz)) {
            return ",\n" + """
|        createAccount.name as createUserName,
|        updateAccount.name as updateUserName
""".trimMargin()
        }
        return ""
    }

    private fun createUpdateAccountJoin(): String {
        val clazz = classModel.clazz ?: return ""
        if (BaseUserEntity::class.java.isAssignableFrom(clazz)) {
            return "\n" + """
|        left join sys_account as createAccount on createAccount.id = ${tableNameAbbr(classModel)}.create_user
|        left join sys_account as updateAccount on updateAccount.id = ${tableNameAbbr(classModel)}.update_user
""".trimMargin()
        }
        return ""
    }

    override fun onModify(compareResultModel: CompareResultModel, content: String): String {
        // TODO: Zhouwx: 2025/8/18 Join
        var content = content
        val addFields = compareResultModel.addFields
        val addForSelectFields = compareResultModel.addForSelect
        content = addIdFields(addFields, content)

        content = addListFields(addFields, content)

        content = addSelectFunctions(addForSelectFields, content)

        content = addIfTests(addFields, content)

        val delForSelect = compareResultModel.delForSelect
        content = deleteRangeFunctionCode(content, "selectFunctions", delForSelect, { fieldModel -> selectListFunctionContent(fieldModel) })

        val delFields = compareResultModel.delFields
        content = deleteRangeFunctionCode(content, "idFields", delFields, { fieldModel -> selectFieldContent(fieldModel) }, afterReplace = { scope ->
            var result = scope.trim()
            if (result.endsWith(",")) {
                result = result.dropLast(1)
            }
            result
        })

        content = deleteRangeFunctionCode(content, "listFields", delFields, { fieldModel -> selectFieldContent(fieldModel) }, afterReplace = { scope ->
            var result = scope.trim()
            if (result.endsWith(",")) {
                result = result.dropLast(1)
            }
            result
        })


        content = deleteRangeFunctionCode(content, "ifTest", delFields, { fieldModel -> ifTestContent(fieldModel) })
        return content
    }

    private fun addListFields(addFields: java.util.ArrayList<FieldModel>, content: String): String {
        var content = insertFieldListContent(content, addFields, "listFields", { index, fieldModel -> selectFieldContent(fieldModel) }, suffix = ",")
        return content
    }

    private fun addIfTests(addFields: ArrayList<FieldModel>, content: String): String {
        val addIndex = content.indexOf(rangeEnd("ifTest"))
        var content = insertFieldListContent(content, addFields, addIndex, { index, fieldModel -> ifTestContent(fieldModel) })
        return content
    }

    private fun addIdFields(addFields: ArrayList<FieldModel>, content: String): String {
        var content = insertFieldListContent(content, addFields, "idFields", { index, fieldModel -> selectFieldContent(fieldModel) }, suffix = ",")
        return content
    }

    private fun addSelectFunctions(addFields: ArrayList<FieldModel>, content: String): String {
        val addIndex = content.indexOf(rangeEnd("selectFunctions"))
        var content = insertFieldListContent(content, addFields, addIndex, { index, fieldModel -> selectListFunctionContent(fieldModel) })
        return content
    }

    private fun selectFunctions(): String {
        return classModel.fieldList.filter {
            it.forSelect
        }.map {
            selectListFunctionContent(it)
        }.joinToString("\n")
    }

    private fun selectListFunctionContent(it: FieldModel) = """
    |    <select id="select${it.nameBigCamel}List" resultType="com.itdct.onflow.core.vo.KeyLabelValueVo">
    |        select 
    |        ${tableNameAbbr(classModel)}.id as `key`,
    |        ${tableNameAbbr(classModel)}.${it.columnName} as `label`,
    |        ${tableNameAbbr(classModel)}.id as `value`
    |        from ${classModel.tableName} as ${tableNameAbbr(classModel)}
    |        <where>
    |            <if test="${it.name} != null${stringNotEmpty(it)}">
    |                and ${tableNameAbbr(classModel)}.${it.columnName} ${compareContent(it)}
    |            </if>
    |        </where>
    |    </select>
    |    
                """.trimMargin()

    private fun stringNotEmpty(it: FieldModel): String {
        return when (it.clazz) {
            String::class.java -> " and ${it.name} != ''"
            else -> ""
        }
    }

    private fun innerJoinTables(): String {
        val content = classModel.fieldList.filter {
            it.targetClass != null
        }.map {
//            val targetClazz = it.targetClass!!.java
//            val entityAnalyse = EntityAnalyse<IdEntity>(generatorConfig)
//            val targetClassModel = entityAnalyse.analyseClass(targetClazz as Class<IdEntity>)
//            val targetFieldName = targetClassModel.fieldList.find { targetField ->
//                targetField.name == it.targetFieldName
//            }
//            if (targetFieldName == null) {
//                throw RuntimeException("${targetClazz.name} 中没有找到 ${it.targetFieldName}")
//            }

            """
|        left join ${it.targetClassTableName} as ${it.targetTableNameAbbr} on ${tableNameAbbr(classModel)}.${it.columnName} = ${it.targetTableNameAbbr}.id
            """.trimMargin()

        }.joinToString("\n")
        if (content.isNotBlank()) {
            return "\n" + content + "\n"
        } else {
            return ""
        }
    }

    private fun innerJoinFields(): String {
        val content = classModel.fieldList.filter {
            it.targetClass != null
        }.map {
            """
|        ${it.targetTableNameAbbr}.${it.targetColumnName} as ${it.name}${it.idGenSuffix}
            """.trimMargin()

        }.joinToString(",\n")
        if (content.isNotBlank()) {
            return ",\n$content"
        } else {
            return ""
        }
    }

    private fun tableNameAbbr(classModel: ClassModel): String {
        return classModel.tableNameAbbr
    }

    private fun selectFields(): String {
        return classModel.fieldList.filter {
            !returnValueFilter.contains(it.name)
        }.map {
            selectFieldContent(it)
        }.joinToString(",\n")
    }

    private fun selectFieldContent(it: FieldModel) = "        ${tableNameAbbr(classModel)}.${it.columnName} as ${it.name}"

    private fun ifTests(): String {
        val content = classModel.fieldList.filter {
            !filterFieldList.contains(it.name)
        }.map {
            ifTestContent(it)
        }.joinToString("\n")
        if (content.isBlank()) {
            return ""
        } else {
            return "\n" + content + "\n"
        }
    }

    private fun ifTestContent(it: FieldModel) = """
    |            <if test="${it.name} != null${testString(it)}">
    |                and ${tableNameAbbr(classModel)}.${it.columnName} ${compareContent(it)}
    |            </if>
                """.trimMargin()

    private fun compareContent(it: FieldModel): String {
        return when (it.clazz) {
            String::class.java -> "like concat('%', #{${it.name}}, '%')"
            else -> "= #{${it.name}}"
        }
    }

    private fun testString(it: FieldModel): String {
        if (it.clazz == String::class.java) {
            return " and ${it.name} != ''"
        }
        return ""
    }

    private fun orderByField(): String {
        classModel.fieldList.any {
            it.name == "createTime"
        }.also {
            if (it) {
                return "create_time"
            }
        }
        return "id"
    }

    override fun belongGroup(): GroupEnum {
        return GroupEnum.MAPPER_XML
    }

    override fun subModuleName(): String {
        return "adaptor"
    }

    override fun getFolderFileName(): String {
        return "${classModel.className}Mapper.xml"
    }
}